﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;

namespace GoodStuff.Web.Controls
{
    /// <summary>
    /// Replacement class for the 'Image' control that uses the CSS Sprite engine to free up bandwidth
    /// </summary>
    /// <todo>
    /// Generate a designer that just displays the real file.
    /// </todo>
    public class Sprite : Control
    {
        /// <summary>
        /// The Relative filename of the sprite.
        /// </summary>
        public string FileName { get; set; }

        protected override void Render(HtmlTextWriter writer)
        {
            // TODO: Have the CSS engine render/modify a sprite image class, and refer to that classname.
            // fornow: inline;

            SpriteEntry entry = SpriteHandler.Sprites[FileName];

            //TODO: Make the version dynamic so that new remote cache understands that files have changed.
            string filename = this.ResolveUrl("~/spritehandler/sprite.axd?version=2121");

            string inlineStyle = string.Format("background-image: url('{0}'); background-position: 0px -{1}px; width: {2}px; height: {3}px",
                filename,
                entry.OffsetY,
                entry.Width,
                entry.Height);

            writer.AddAttribute(HtmlTextWriterAttribute.Style, inlineStyle);
            writer.AddAttribute(HtmlTextWriterAttribute.Width, entry.Width.ToString());
            writer.AddAttribute(HtmlTextWriterAttribute.Height, entry.Height.ToString());
            writer.AddAttribute(HtmlTextWriterAttribute.Src, "");
            writer.RenderBeginTag(HtmlTextWriterTag.Img);
            writer.RenderEndTag();
        }
    }
    
    public class SpriteHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(30));
            context.Response.ContentType = "image/png";
            context.Response.OutputStream.Write(Sprites.GeneratedImage, 0, Sprites.GeneratedImage.Length);
        }

        private static SpriteCollection _sprites;
        public static SpriteCollection Sprites
        {
            get
            {
                lock (typeof(SpriteHandler))
                {
                    if (_sprites == null)
                    {
                        _sprites = new SpriteCollection(HttpContext.Current.Server.MapPath("~/sprites"));
                    }
                }
                return _sprites;
            }
        }
    }

    public class SpriteEntry
    {
        public string Filename { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public int OffsetX { get; set; }
        public int OffsetY { get; set; }
        public System.Drawing.Image Image { get; set; }
    }

    public class SpriteCollection
    {
        private Dictionary<string, SpriteEntry> _sprites = new Dictionary<string, SpriteEntry>();

        public SpriteCollection(string rootFolder)
        {
            int offsetY = 0;
            int maxWidth = 0;
            foreach (string path in System.IO.Directory.GetFiles(rootFolder, "*.png"))
            {
                //load the image into memory.
                Image img = System.Drawing.Image.FromFile(path);
                //do not dispose or it will be gone.

                string filename = System.IO.Path.GetFileName(path);
                _sprites.Add(filename, new SpriteEntry()
                    {
                        Filename = filename,
                        Image = img,
                        Width = img.Size.Width,
                        Height = img.Size.Height,
                        OffsetX = 0,
                        OffsetY = offsetY
                    });
                offsetY += img.Size.Height;
            }

            maxWidth = _sprites.Values.Max(p => p.Width);

            //now we have all the images, construct the cached SPRITE.

            using(Bitmap bigSprite = new System.Drawing.Bitmap(maxWidth, offsetY))
            {
                offsetY = 0;
                using (Graphics g = Graphics.FromImage(bigSprite))
                {
                    foreach (var item in _sprites.Values)
                    {
                        g.DrawImage(item.Image, 0, item.OffsetY, item.Width, item.Height);
                        item.Image.Dispose();
                        item.Image = null;
                    }
                }

                //now dump the sprite to cache and dispose of the list.
                using(MemoryStream ms = new MemoryStream())
                {
                    bigSprite.Save(ms, ImageFormat.Png);
                    GeneratedImage = ms.GetBuffer();
                }
            }
        }

        public byte[] GeneratedImage{ get; set; }

        public SpriteEntry this[string filename]
        {
            get
            {
                return _sprites[filename];
            }
        }
    }

}
